namespace SubSonic
{
    using SubSonic.Sugar;
    using SubSonic.Utilities;
    using System;
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using System.Data;
    using System.Reflection;

    [Serializable]
    public class TableSchema
    {
        [Serializable]
        public abstract class AbstractTableSchema
        {
            private string _tableName;
            private string className;
            private string classNamePlural;
            private string displayName;
            private TableSchema.ExtendedPropertyCollection extendedProperties = new TableSchema.ExtendedPropertyCollection();
            private string propertyName;
            private DataProvider provider;
            private string schemaName;
            private SubSonic.TableType tableType;

            protected AbstractTableSchema()
            {
            }

            public void ApplyExtendedProperties()
            {
                TableSchema.ExtendedProperty extendedProperty = TableSchema.ExtendedProperty.GetExtendedProperty(this.ExtendedProperties, "SSX_TABLE_CLASS_NAME_SINGULAR");
                TableSchema.ExtendedProperty property2 = TableSchema.ExtendedProperty.GetExtendedProperty(this.ExtendedProperties, "SSX_TABLE_CLASS_NAME_PLURAL");
                TableSchema.ExtendedProperty property3 = TableSchema.ExtendedProperty.GetExtendedProperty(this.ExtendedProperties, "SSX_TABLE_DISPLAY_NAME");
                if (extendedProperty != null)
                {
                    this.className = extendedProperty.PropertyValue;
                }
                if (property2 != null)
                {
                    this.classNamePlural = property2.PropertyValue;
                }
                if (property3 != null)
                {
                    this.displayName = property3.PropertyValue;
                }
            }

            public static string TransformClassName(string name, bool isPlural, SubSonic.TableType tableType, DataProvider provider)
            {
                if (string.IsNullOrEmpty(name))
                {
                    return string.Empty;
                }
                string inputString = name;
                if (tableType == SubSonic.TableType.Table)
                {
                    inputString = Utility.StripText(inputString, provider.StripTableText);
                }
                else if (tableType == SubSonic.TableType.View)
                {
                    inputString = Utility.StripText(inputString, provider.StripViewText);
                }
                inputString = Utility.GetProperName(Utility.RegexTransform(inputString, provider), provider);
                inputString = Utility.IsStringNumeric(inputString) ? ("_" + inputString) : inputString;
                inputString = Utility.StripNonAlphaNumeric(inputString).Trim();
                if (!isPlural)
                {
                    inputString = provider.FixPluralClassNames ? Utility.PluralToSingular(inputString) : inputString;
                }
                return Utility.KeyWordCheck(inputString, string.Empty, provider);
            }

            public string ClassName
            {
                get
                {
                    return this.className;
                }
                protected set
                {
                    this.className = value;
                }
            }

            public string ClassNamePlural
            {
                get
                {
                    return this.classNamePlural;
                }
                protected set
                {
                    this.classNamePlural = value;
                }
            }

            public string DisplayName
            {
                get
                {
                    return this.displayName;
                }
                protected set
                {
                    this.displayName = value;
                }
            }

            public TableSchema.ExtendedPropertyCollection ExtendedProperties
            {
                get
                {
                    return this.extendedProperties;
                }
                set
                {
                    this.extendedProperties = value;
                }
            }

            public string Name
            {
                get
                {
                    return this._tableName;
                }
                set
                {
                    this.TableName = value;
                }
            }

            public string PropertyName
            {
                get
                {
                    return this.propertyName;
                }
                protected set
                {
                    this.propertyName = value;
                }
            }

            public DataProvider Provider
            {
                get
                {
                    return this.provider;
                }
                protected set
                {
                    this.provider = value;
                }
            }

            public string SchemaName
            {
                get
                {
                    return this.schemaName;
                }
                set
                {
                    this.schemaName = value;
                }
            }

            public string TableName
            {
                get
                {
                    return this._tableName;
                }
                set
                {
                    this._tableName = value;
                    this.className = TransformClassName(this.Name, false, this.tableType, this.Provider);
                    this.classNamePlural = TransformClassName(this.Name, true, this.tableType, this.Provider);
                    this.displayName = Utility.ParseCamelToProper(this.ClassName);
                }
            }

            public SubSonic.TableType TableType
            {
                get
                {
                    return this.tableType;
                }
                set
                {
                    this.tableType = value;
                }
            }
        }

        public class ExtendedProperty
        {
            private string propertyName;
            private string propertyValue;

            public ExtendedProperty(string name, string value)
            {
                this.propertyName = name;
                this.propertyValue = value;
            }

            public static TableSchema.ExtendedProperty GetExtendedProperty(TableSchema.ExtendedPropertyCollection exPropCol, string extendedPropertyName)
            {
                TableSchema.ExtendedProperty property = null;
                if (exPropCol.Contains(extendedPropertyName))
                {
                    property = exPropCol[extendedPropertyName];
                    if (string.IsNullOrEmpty(property.PropertyValue))
                    {
                        property = null;
                    }
                }
                return property;
            }

            public string PropertyName
            {
                get
                {
                    return this.propertyName;
                }
                set
                {
                    this.propertyName = value;
                }
            }

            public string PropertyValue
            {
                get
                {
                    return this.propertyValue;
                }
                set
                {
                    this.propertyValue = value;
                }
            }
        }

        public class ExtendedPropertyCollection : KeyedCollection<string, TableSchema.ExtendedProperty>
        {
            protected override string GetKeyForItem(TableSchema.ExtendedProperty extendedProperty)
            {
                return extendedProperty.PropertyName;
            }
        }

        [Serializable]
        public class ForeignKeyTable : TableSchema.AbstractTableSchema
        {
            private string _columnName;

            public ForeignKeyTable(DataProvider dataProvider)
            {
                base.Provider = dataProvider;
            }

            public string ColumnName
            {
                get
                {
                    return this._columnName;
                }
                set
                {
                    this._columnName = value;
                    base.PropertyName = TableSchema.TableColumn.TransformPropertyName(this._columnName, base.TableName, base.Provider);
                }
            }
        }

        public class ForeignKeyTableCollection : List<TableSchema.ForeignKeyTable>
        {
        }

        [Serializable]
        public class ManyToManyDetails
        {
            private string linksToColum;
            private string linksToTable;
            private string mapTableName;

            public string LinksToColumn
            {
                get
                {
                    return this.linksToColum;
                }
                set
                {
                    this.linksToColum = value;
                }
            }

            public string LinksToTable
            {
                get
                {
                    return this.linksToTable;
                }
                set
                {
                    this.linksToTable = value;
                }
            }

            public string MapTableName
            {
                get
                {
                    return this.mapTableName;
                }
                set
                {
                    this.mapTableName = value;
                }
            }
        }

        [Serializable]
        public class ManyToManyDetailsCollection : List<TableSchema.ManyToManyDetails>
        {
        }

        [Serializable]
        public class ManyToManyRelationship : TableSchema.AbstractTableSchema
        {
            private string foreignPrimaryKey;
            private string foreignTableName;
            private string mapTableForeignTableKeyColumn;
            private string mapTableLocalTableKeyColumn;
            private readonly string mapTableName;

            public ManyToManyRelationship(string tableName, DataProvider dataProvider)
            {
                base.Provider = dataProvider;
                this.mapTableName = tableName;
                base.Name = this.mapTableName;
                base.ClassName = TableSchema.AbstractTableSchema.TransformClassName(base.Name, false, base.TableType, base.Provider);
                base.ClassNamePlural = TableSchema.AbstractTableSchema.TransformClassName(base.Name, false, base.TableType, base.Provider);
                base.DisplayName = Utility.ParseCamelToProper(base.ClassName);
            }

            public string ForeignPrimaryKey
            {
                get
                {
                    return this.foreignPrimaryKey;
                }
                set
                {
                    this.foreignPrimaryKey = value;
                }
            }

            public string ForeignTableName
            {
                get
                {
                    return this.foreignTableName;
                }
                set
                {
                    this.foreignTableName = value;
                }
            }

            public string MapTableForeignTableKeyColumn
            {
                get
                {
                    return this.mapTableForeignTableKeyColumn;
                }
                set
                {
                    this.mapTableForeignTableKeyColumn = value;
                }
            }

            public string MapTableLocalTableKeyColumn
            {
                get
                {
                    return this.mapTableLocalTableKeyColumn;
                }
                set
                {
                    this.mapTableLocalTableKeyColumn = value;
                }
            }

            public string MapTableName
            {
                get
                {
                    return this.mapTableName;
                }
            }
        }

        [Serializable]
        public class ManyToManyRelationshipCollection : List<TableSchema.ManyToManyRelationship>
        {
        }

        [Serializable]
        public class PrimaryKeyTable : TableSchema.AbstractTableSchema
        {
            private string _columnName;

            public PrimaryKeyTable(DataProvider dataProvider)
            {
                base.Provider = dataProvider;
            }

            public string ColumnName
            {
                get
                {
                    return this._columnName;
                }
                set
                {
                    this._columnName = value;
                    base.PropertyName = TableSchema.TableColumn.TransformPropertyName(this._columnName, base.TableName, base.Provider);
                }
            }
        }

        public class PrimaryKeyTableCollection : List<TableSchema.PrimaryKeyTable>
        {
        }

        [Serializable]
        public class Table : TableSchema.AbstractTableSchema
        {
            private TableSchema.ForeignKeyTableCollection _foreignKeys;
            private bool _hasManyToMany;
            private TableSchema.PrimaryKeyTableCollection _primaryKeys;
            private TableSchema.TableColumnCollection columns;
            private TableSchema.ManyToManyRelationshipCollection manyToManys;
            private TableSchema.TableColumn primaryKey;
            private TableSchema.TableColumn[] primaryKeys;

            public Table(DataProvider dataProvider)
            {
                this.manyToManys = new TableSchema.ManyToManyRelationshipCollection();
                this._primaryKeys = new TableSchema.PrimaryKeyTableCollection();
                this._foreignKeys = new TableSchema.ForeignKeyTableCollection();
                base.Provider = dataProvider;
            }

            public Table(string tableName, TableType tblType, DataProvider dataProvider)
            {
                this.manyToManys = new TableSchema.ManyToManyRelationshipCollection();
                this._primaryKeys = new TableSchema.PrimaryKeyTableCollection();
                this._foreignKeys = new TableSchema.ForeignKeyTableCollection();
                base.Provider = dataProvider;
                base.TableType = tblType;
                base.Name = tableName;
            }

            public TableSchema.TableColumn GetColumn(string columnName)
            {
                foreach (TableSchema.TableColumn column2 in this.Columns)
                {
                    if (Utility.IsMatch(column2.ColumnName.Trim(), columnName.Trim()))
                    {
                        return column2;
                    }
                }
                return null;
            }

            public bool HasForeignKeys()
            {
                return (this.ForeignKeys.Count > 0);
            }

            public TableSchema.TableColumnCollection Columns
            {
                get
                {
                    return this.columns;
                }
                set
                {
                    this.columns = value;
                }
            }

            public TableSchema.ForeignKeyTableCollection ForeignKeys
            {
                get
                {
                    return this._foreignKeys;
                }
                set
                {
                    this._foreignKeys = value;
                }
            }

            public bool HasManyToMany
            {
                get
                {
                    return this._hasManyToMany;
                }
                set
                {
                    this._hasManyToMany = value;
                }
            }

            public TableSchema.ManyToManyRelationshipCollection ManyToManys
            {
                get
                {
                    return this.manyToManys;
                }
                set
                {
                    this.manyToManys = value;
                }
            }

            public TableSchema.TableColumn PrimaryKey
            {
                get
                {
                    if (this.columns != null)
                    {
                        this.primaryKey = this.columns.GetPrimaryKey();
                    }
                    return this.primaryKey;
                }
            }

            public TableSchema.TableColumn[] PrimaryKeys
            {
                get
                {
                    if (this.columns != null)
                    {
                        this.primaryKeys = this.columns.GetPrimaryKeys();
                    }
                    return this.primaryKeys;
                }
            }

            public TableSchema.PrimaryKeyTableCollection PrimaryKeyTables
            {
                get
                {
                    return this._primaryKeys;
                }
                set
                {
                    this._primaryKeys = value;
                }
            }
        }

        [Serializable]
        public class TableCollection : List<TableSchema.Table>
        {
            public TableSchema.Table this[string tableName]
            {
                get
                {
                    foreach (TableSchema.Table table2 in this)
                    {
                        if (Utility.IsMatch(table2.Name, tableName))
                        {
                            return table2;
                        }
                    }
                    return null;
                }
                set
                {
                    int num = 0;
                    foreach (TableSchema.Table table in this)
                    {
                        if (Utility.IsMatch(table.Name, tableName))
                        {
                            base[num] = value;
                            break;
                        }
                        num++;
                    }
                }
            }
        }

        [Serializable]
        public class TableColumn
        {
            private string argumentName;
            private bool autoIncrement;
            private string columnName;
            private DbType dbType;
            private string defaultSetting;
            private string displayName;
            private TableSchema.ExtendedPropertyCollection extendedProperties = new TableSchema.ExtendedPropertyCollection();
            private string foreignKeyTableName;
            private bool isForeignKey;
            private bool isNullable;
            private bool isPrimaryKey;
            private bool isReadOnly;
            private int maxLength;
            private int numberPrecision;
            private int numberScale;
            private string parameterName;
            private string propertyName;
            private string schemaName;
            private readonly SubSonic.TableSchema.Table table;

            public TableColumn(SubSonic.TableSchema.Table tableSchema)
            {
                this.table = tableSchema;
            }

            public void ApplyExtendedProperties()
            {
                TableSchema.ExtendedProperty extendedProperty = TableSchema.ExtendedProperty.GetExtendedProperty(this.ExtendedProperties, "SSX_COLUMN_PROPERTY_NAME");
                TableSchema.ExtendedProperty property2 = TableSchema.ExtendedProperty.GetExtendedProperty(this.ExtendedProperties, "SSX_COLUMN_DISPLAY_NAME");
                if (extendedProperty != null)
                {
                    this.propertyName = extendedProperty.PropertyValue;
                }
                if (property2 != null)
                {
                    this.displayName = property2.PropertyValue;
                }
            }

            public Type GetPropertyType()
            {
                return Type.GetType(Utility.GetSystemType(this.dbType));
            }

            public static string TransformPropertyName(string name, string table, DataProvider provider)
            {
                if (string.IsNullOrEmpty(name))
                {
                    return string.Empty;
                }
                string inputString = name;
                inputString = Utility.GetProperName(Utility.RegexTransform(Utility.StripText(inputString, provider.StripColumnText), provider), provider);
                inputString = Utility.IsStringNumeric(inputString) ? ("_" + inputString) : inputString;
                return Utility.KeyWordCheck(Utility.StripNonAlphaNumeric(inputString).Trim(), table, provider);
            }

            public string ArgumentName
            {
                get
                {
                    return this.argumentName;
                }
            }

            public bool AutoIncrement
            {
                get
                {
                    return this.autoIncrement;
                }
                set
                {
                    this.autoIncrement = value;
                }
            }

            public string ColumnName
            {
                get
                {
                    return this.columnName;
                }
                set
                {
                    this.columnName = value;
                    string columnName = this.columnName;
                    this.propertyName = TransformPropertyName(columnName, this.Table.ClassName, this.table.Provider);
                    this.displayName = TransformPropertyName(columnName, this.Table.ClassName, this.table.Provider);
                    this.displayName = Utility.ParseCamelToProper(this.displayName);
                    if ((!Validation.IsUpperCase(this.displayName) && (this.IsPrimaryKey || this.IsForeignKey)) && (this.displayName.Length > 1))
                    {
                        string stringA = this.displayName.Substring(this.displayName.Length - 2, 2);
                        if (Utility.IsMatch(stringA, "id"))
                        {
                            char ch = stringA[0];
                            if (ch.ToString() == "I")
                            {
                                this.displayName = this.displayName.Substring(0, this.displayName.Length - 2);
                            }
                        }
                    }
                    this.parameterName = Utility.PrefixParameter(this.columnName, this.Table.Provider);
                    this.argumentName = "var" + this.propertyName;
                }
            }

            public DbType DataType
            {
                get
                {
                    return this.dbType;
                }
                set
                {
                    this.dbType = value;
                }
            }

            public string DefaultSetting
            {
                get
                {
                    return this.defaultSetting;
                }
                set
                {
                    this.defaultSetting = value;
                }
            }

            public string DisplayName
            {
                get
                {
                    return this.displayName;
                }
            }

            public TableSchema.ExtendedPropertyCollection ExtendedProperties
            {
                get
                {
                    return this.extendedProperties;
                }
                set
                {
                    this.extendedProperties = value;
                }
            }

            public string ForeignKeyTableName
            {
                get
                {
                    return this.foreignKeyTableName;
                }
                set
                {
                    this.foreignKeyTableName = value;
                }
            }

            public bool IsDateTime
            {
                get
                {
                    if ((this.DataType != DbType.DateTime) && (this.DataType != DbType.Time))
                    {
                        return (this.DataType == DbType.Date);
                    }
                    return true;
                }
            }

            public bool IsForeignKey
            {
                get
                {
                    return this.isForeignKey;
                }
                set
                {
                    this.isForeignKey = value;
                }
            }

            public bool IsNullable
            {
                get
                {
                    return this.isNullable;
                }
                set
                {
                    this.isNullable = value;
                }
            }

            public bool IsNumeric
            {
                get
                {
                    if (((((this.DataType != DbType.Currency) && (this.DataType != DbType.Decimal)) && ((this.DataType != DbType.Double) && (this.DataType != DbType.Int16))) && (((this.DataType != DbType.Int32) && (this.DataType != DbType.Int64)) && ((this.DataType != DbType.Single) && (this.DataType != DbType.UInt16)))) && ((this.DataType != DbType.UInt32) && (this.DataType != DbType.UInt64)))
                    {
                        return (this.DataType == DbType.VarNumeric);
                    }
                    return true;
                }
            }

            public bool IsPrimaryKey
            {
                get
                {
                    return this.isPrimaryKey;
                }
                set
                {
                    this.isPrimaryKey = value;
                }
            }

            public bool IsReadOnly
            {
                get
                {
                    return this.isReadOnly;
                }
                set
                {
                    this.isReadOnly = value;
                }
            }

            public bool IsString
            {
                get
                {
                    if (((this.DataType != DbType.AnsiString) && (this.DataType != DbType.AnsiStringFixedLength)) && (this.DataType != DbType.String))
                    {
                        return (this.DataType == DbType.StringFixedLength);
                    }
                    return true;
                }
            }

            public int MaxLength
            {
                get
                {
                    return this.maxLength;
                }
                set
                {
                    this.maxLength = value;
                }
            }

            public int NumberPrecision
            {
                get
                {
                    return this.numberPrecision;
                }
                set
                {
                    this.numberPrecision = value;
                }
            }

            public int NumberScale
            {
                get
                {
                    return this.numberScale;
                }
                set
                {
                    this.numberScale = value;
                }
            }

            public string ParameterName
            {
                get
                {
                    return this.parameterName;
                }
            }

            public string PropertyName
            {
                get
                {
                    return this.propertyName;
                }
            }

            public string SchemaName
            {
                get
                {
                    return this.schemaName;
                }
                set
                {
                    this.schemaName = value;
                }
            }

            public SubSonic.TableSchema.Table Table
            {
                get
                {
                    return this.table;
                }
            }
        }

        [Serializable]
        public class TableColumnCollection : List<TableSchema.TableColumn>
        {
            public void Add(TableSchema.Table tbl, string name, DbType dbType, bool isNullable)
            {
                this.Add(tbl, name, dbType, isNullable, false, false);
            }

            public void Add(TableSchema.Table tbl, string name, DbType dbType, bool isNullable, bool isPrimaryKey, bool isForeignKey)
            {
                TableSchema.TableColumn item = new TableSchema.TableColumn(tbl);
                item.IsPrimaryKey = isPrimaryKey;
                item.IsForeignKey = isForeignKey;
                item.IsNullable = isNullable;
                item.DataType = dbType;
                item.ColumnName = name;
                if (!this.Contains(name))
                {
                    base.Add(item);
                }
            }

            public bool Contains(string columnName)
            {
                foreach (TableSchema.TableColumn column in this)
                {
                    if (Utility.IsMatch(column.ColumnName, columnName))
                    {
                        return true;
                    }
                }
                return false;
            }

            public TableSchema.TableColumn GetColumn(string columnName)
            {
                foreach (TableSchema.TableColumn column2 in this)
                {
                    if (Utility.IsMatch(column2.ColumnName, columnName))
                    {
                        return column2;
                    }
                }
                return null;
            }

            public TableSchema.TableColumn GetPrimaryKey()
            {
                foreach (TableSchema.TableColumn column2 in this)
                {
                    if (column2.IsPrimaryKey)
                    {
                        return column2;
                    }
                }
                return null;
            }

            public TableSchema.TableColumn[] GetPrimaryKeys()
            {
                List<TableSchema.TableColumn> list = new List<TableSchema.TableColumn>();
                foreach (TableSchema.TableColumn column in this)
                {
                    if (column.IsPrimaryKey)
                    {
                        list.Add(column);
                    }
                }
                return list.ToArray();
            }
        }

        [Serializable]
        public class TableColumnSetting
        {
            private readonly string _columnName;
            private object _currentValue;
            private bool _isDirty;

            public TableColumnSetting(string columnName, object currentValue)
            {
                this._columnName = columnName;
                this._currentValue = currentValue;
            }

            public string ColumnName
            {
                get
                {
                    return this._columnName;
                }
            }

            public object CurrentValue
            {
                get
                {
                    return this._currentValue;
                }
                set
                {
                    if (((value != null) || (this._currentValue != null)) && ((value == null) || !value.Equals(this._currentValue)))
                    {
                        this._currentValue = value;
                        this._isDirty = true;
                    }
                }
            }

            public bool IsDirty
            {
                get
                {
                    return this._isDirty;
                }
                set
                {
                    this._isDirty = value;
                }
            }
        }

        [Serializable]
        public class TableColumnSettingCollection : KeyedCollection<string, TableSchema.TableColumnSetting>
        {
            protected override string GetKeyForItem(TableSchema.TableColumnSetting item)
            {
                return item.ColumnName;
            }

            public object GetValue(string columnName)
            {
                return base[columnName.ToLower()].CurrentValue;
            }

            public T GetValue<T>(string columnName)
            {
                object currentValue;
                columnName = columnName.ToLower();
                try
                {
                    currentValue = base[columnName].CurrentValue;
                }
                catch
                {
                    throw new Exception("There's no column called '" + columnName + "' for this object");
                }
                if ((currentValue == null) || (currentValue == DBNull.Value))
                {
                    return default(T);
                }
                Type objType = typeof(T);
                if (IsNullable(objType) || (objType == typeof(object)))
                {
                    return (T) currentValue;
                }
                Type type = currentValue.GetType();
                if (type == typeof(byte[]))
                {
                    return (T) Convert.ChangeType(currentValue, type);
                }
                return (T) Convert.ChangeType(currentValue, objType);
            }

            public static bool IsNullable(Type objType)
            {
                if (!objType.IsGenericType)
                {
                    return false;
                }
                return objType.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
            }

            public void SetValue(string columnName, object oVal)
            {
                columnName = columnName.ToLower();
                if (!base.Contains(columnName))
                {
                    base.Add(new TableSchema.TableColumnSetting(columnName, oVal));
                }
                else
                {
                    base[columnName].CurrentValue = oVal;
                }
            }

            public bool IsDirty
            {
                get
                {
                    foreach (TableSchema.TableColumnSetting setting in this)
                    {
                        if (setting.IsDirty)
                        {
                            return true;
                        }
                    }
                    return false;
                }
                set
                {
                    foreach (TableSchema.TableColumnSetting setting in this)
                    {
                        setting.IsDirty = value;
                    }
                }
            }
        }
    }
}

